//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
///
//==================================================================

#include "stdafx.h"
#include "m5d_displist.h"
#include "m5d_scene.h"

//==================================================================
using namespace M5D;
using namespace PSYSGFX;

//#define PARANOIA_CHECKS

//==================================================================
//==
//==================================================================
#define VMERGE_NOMATCH_FLAG		0x80000000
#define VMERGE_MAXINDICES		4			// pos, tex, col nor

//==================================================================
struct VtxBundle
{
	union
	{
		int	_v[4];
		
		struct
		{
			int	_pa0;
			int	_pa1;
			int	_pa2;
			int	_pa3;
		};
	};

	void Reset()
	{
		_pa0 = -1;
		_pa1 = -1;
		_pa2 = -1;
		_pa3 = -1;
	}

	//==================================================================
	PFORCEINLINE const int &operator[] (size_t index) const
	{
		return _v[ index ];
	}
	//==================================================================
	PFORCEINLINE int &operator[] (size_t index)
	{
		return _v[ index ];
	}

	PFORCEINLINE bool operator==(const VtxBundle &other) const
	{
		return	_pa0 == other._pa0 &&
				_pa1 == other._pa1 &&
				_pa2 == other._pa2 &&
				_pa3 == other._pa3;
	}

	PFORCEINLINE bool operator!=(const VtxBundle &other) const
	{
		return	!(*this == other);
	}
};
//==================================================================
struct VMERGE_t
{
	std::vector<VtxBundle>	idxs;
};


#if VMERGE_MAXINDICES != 4 
#error noooooooooooo !!!!!!!!
#endif

//==================================================================
static int VMERGE_find_vert_sub( VMERGE_t *p, int top, int bottom, const VtxBundle &indices )
{
	// found nothing.. escape
	if ( top >= bottom )
	{
		if ( bottom >= p->idxs.size() )
			return top | VMERGE_NOMATCH_FLAG;

#ifdef PARANOIA_CHECKS
		PASSERT( top >= 0 && top < p->idxs.size() );
#endif
		if ( p->idxs[ top ] != indices )
			return top | VMERGE_NOMATCH_FLAG;
		else
			return top;
	}

	int	center = top + bottom >> 1;
#ifdef PARANOIA_CHECKS
	PASSERT( top >= 0 && top < p->idxs.size() );
	PASSERT( center >= 0 && center < p->idxs.size() );
	PASSERT( bottom >= 0 && bottom <= p->idxs.size() );
#endif

	const VtxBundle	&cindices = p->idxs[ center ];

	if ( indices._pa0 < cindices._pa0 )		return VMERGE_find_vert_sub( p, top, center, indices );
	if ( indices._pa0 > cindices._pa0 )		return VMERGE_find_vert_sub( p, center+1, bottom, indices );
	// no winner for elem 0.. let's try elem 1
	if ( indices._pa1 < cindices._pa1 )		return VMERGE_find_vert_sub( p, top, center, indices );
	if ( indices._pa1 > cindices._pa1 )		return VMERGE_find_vert_sub( p, center+1, bottom, indices );
	// no winner for elem 1.. let's try elem 2
	if ( indices._pa2 < cindices._pa2 )		return VMERGE_find_vert_sub( p, top, center, indices );
	if ( indices._pa2 > cindices._pa2 )		return VMERGE_find_vert_sub( p, center+1, bottom, indices );
	// no winner for elem 2.. let's try elem 3
	if ( indices._pa3 < cindices._pa3 )		return VMERGE_find_vert_sub( p, top, center, indices );
	if ( indices._pa3 > cindices._pa3 )		return VMERGE_find_vert_sub( p, center+1, bottom, indices );

	// no winners at all.. element found !
	return center;
}

//==================================================================
void VMERGE_add_vert( VMERGE_t *p, const VtxBundle *idxs )
{
int		tablepos, flags;
int		cur_n_idxs;

	// make sure there is no conflict with bits used for messaging !!
#ifdef PARANOIA_CHECKS
	PASSERT( !(p->idxs.size() & VMERGE_NOMATCH_FLAG) );
#endif

	if ( p->idxs.size() > 0 )
	{
		tablepos = VMERGE_find_vert_sub( p, 0, p->idxs.size(), *idxs );

		flags = tablepos & VMERGE_NOMATCH_FLAG;
		tablepos = tablepos & ~VMERGE_NOMATCH_FLAG;

		if NOT( flags & VMERGE_NOMATCH_FLAG )	// found, no need to add
			return;
	}
	else
		tablepos = 0;

//	cur_n_idxs = p->idxs.size();

//	p->idxs.resize( cur_n_idxs+1 );

#ifdef PARANOIA_CHECKS
	PASSERT( tablepos >= 0 && tablepos <= cur_n_idxs );
#endif

	//p->idxs.insert( p->idxs.begin() + tablepos, *idxs );

	int	n = p->idxs.size();
	p->idxs.resize( n+1 );
	if ( tablepos < n )
	{
		int	n_move = n - tablepos;
		memmove( &p->idxs[ tablepos+1 ], &p->idxs[ tablepos ], sizeof(VtxBundle) * n_move );
	}
	p->idxs[ tablepos ] = *idxs;

/*
	int	n = (cur_n_idxs - tablepos);

	if ( tablepos < cur_n_idxs )
	{
		// move from bottom to top !
		/*
		for (int i=tablepos+n-1; i >= tablepos; --i)
		{
			for (int j=0; j < VMERGE_MAXINDICES; ++j)
				p->idxs[ i + 1 ][ j ] = p->idxs[ i ][ j ];
		}
		* /
		u_int	size = sizeof(p->idxs[0]) * VMERGE_MAXINDICES;

		memmove( &p->idxs[ tablepos+1 ], &p->idxs[ tablepos ], size * n );
	}

	p->idxs[ tablepos ]._pa0 = idxs->_pa0;
	p->idxs[ tablepos ]._pa1 = idxs->_pa1;
	p->idxs[ tablepos ]._pa2 = idxs->_pa2;
	p->idxs[ tablepos ]._pa3 = idxs->_pa3;
*/
}

//==================================================================
//==
//==================================================================
DispListBase::DispListBase( PackVert::Type packvert_type, int max_verts )
{
	if ( packvert_type != PackVert::TYPE_ANY && max_verts > 0 )
		ReallocPackVerts( packvert_type, max_verts );
}

//==================================================================
DispListBase::DispListBase( const DispListBase *src_displistbasep ) :
	_vert_def(src_displistbasep->_vert_def)
{
}

//==================================================================
void DispListBase::ReallocPackVerts( PackVert::Type packvert_type, int max_verts ) throw(...)
{
	static u_char map_ptcn_usage[PackVert::TYPE_TOTAL][4] =
	{
		0,0,0,0,	// PackVert::TYPE_ANY
		1,1,1,1,	// PackVert::TYPE_POSCOLTEXNOR,
		1,1,1,0,	// PackVert::TYPE_POSTEXCOL,
		1,1,0,1,	// PackVert::TYPE_POSTEXNOR,
		1,0,1,1,	// PackVert::TYPE_POSCOLNOR,
		1,1,0,0,	// PackVert::TYPE_POSTEX,
		1,0,1,0,	// PackVert::TYPE_POSCOL,
		1,0,0,1,	// PackVert::TYPE_POSNOR,
		1,0,0,0,	// PackVert::TYPE_POS,
	};
	static int ptcn_std_formats[4] =
	{
		DListVertPool::FORMAT_FLOAT,
		DListVertPool::FORMAT_FLOAT,
		DListVertPool::FORMAT_BYTE,
		DListVertPool::FORMAT_FLOAT,
	};

	PASSERT( packvert_type >= 0 && packvert_type < PackVert::TYPE_TOTAL );

	_vert_def.Reset();

	for (int i=0; i < VType::N; ++i)
	{
		if ( map_ptcn_usage[ packvert_type ][i] )
			_vert_def.AddItem( ptcn_std_formats[i], (VType::VType)i );
	}

	if ( max_verts > 0 )
		_vert_def.AllocateData( max_verts );
}

//==================================================================
/// Triangle Display List class.
//==================================================================
DispList::DispList( class Scene *scenep, PackVert::Type packvert_type, int max_verts, u_int max_prim_indices ) :
	DispListBase( packvert_type, max_verts ),
	_scenep(scenep),
	_prim_indices_datap(NULL),
	_max_prim_indices(0),
	_n_pools(0),
	_max_used_verts(0)
{
	if ( max_prim_indices )
		AllocIndices( max_prim_indices );
}

//==================================================================
DispList::DispList( class Scene *scenep, const DispList *src_displistp ) :
	DispListBase( src_displistp ),
	_scenep(scenep),
	_prim_indices_datap(NULL),
	_max_prim_indices(0),
	_n_pools(0),
	_max_used_verts(0)
{
	AllocIndices( src_displistp->_max_prim_indices );

	_n_pools = src_displistp->_n_pools;
	for (int i=0; i < _n_pools; ++i)
		_pools[i] = src_displistp->_pools[i];

	_max_used_verts = src_displistp->_max_used_verts;
}

//==================================================================
void DispList::BuildVtxFromIdx( const DispList *src_idxlistp ) throw(...)
{
	/*
	PASSERT( src_idxlistp->GetMaxUsedIndices() > 0 );

	// free vertex data and eventual index data as well
	freeDList();

	_vert_def.Reset();
	_vert_def.CopyDefinitions( src_idxlistp->_vert_def );
	_vert_def.AllocateData( src_idxlistp->GetMaxUsedIndices() * 3 );

	u_int	*src_idxp = src_idxlistp->GetFirstIndexPtr();

	for (int i=0; i < src_idxlistp->_n_pools; ++i)
		_pools[i] = src_idxlistp->_pools[i];

	src_idxlistp->GetMaxUsedIndices()
	*/
	PASSERT( 0 );
}

//==================================================================
DispList::Pool *DispList::AddPrimPool( Material *matep, Primitive::Type prim_type, int n_prim_verts )
{
	if ERR_FALSE( _n_pools < MAX_PRIMS_POOLS )
		return NULL;

	Pool	*newpollp = &_pools[ _n_pools ];

	newpollp->_pool_prim_type = prim_type;
	newpollp->_pool_materialp = matep;
	newpollp->_pool_n_verts = n_prim_verts;

	int	cur_max_used_prim_verts = GetMaxUsedPrimVerts();
	int	new_max_used_prim_verts_bottom = cur_max_used_prim_verts + n_prim_verts;

	newpollp->_pool_prims_offset = cur_max_used_prim_verts;

	if ( IsIndexType() )
	{
		if ERR_FALSE( new_max_used_prim_verts_bottom <= GetMaxIndices() )
			return NULL;

		_max_used_verts = GetMaxVerts();
	}
	else
	{
		if ERR_FALSE( new_max_used_prim_verts_bottom <= GetMaxVerts() )
			return NULL;

		_max_used_verts = cur_max_used_prim_verts + n_prim_verts;
	}

	++_n_pools;

	return newpollp;
}

//==================================================================
DispList::Pool *DispList::addPrimPoolRaw( Material *matep, Primitive::Type prim_type, int n_prim_verts )
{
	if ERR_FALSE( _n_pools < MAX_PRIMS_POOLS )
		return NULL;

	Pool	*newpollp = &_pools[ _n_pools ];

	newpollp->_pool_prims_offset = 0;
	newpollp->_pool_prim_type = prim_type;
	newpollp->_pool_materialp = matep;
	newpollp->_pool_n_verts = n_prim_verts;

	++_n_pools;

	return newpollp;
}

//==================================================================
DispList::Pool	*DispList::FindPrimPool( Material *matep, Primitive::Type prim_type )
{
	for (int i=0; i < _n_pools; ++i)
	{
		if ( _pools[i]._pool_materialp == matep && _pools[i]._pool_prim_type == prim_type )
			return &_pools[i];
	}

	return NULL;
}

//==================================================================
int DispList::FindPrimPoolIdx( Material *matep, Primitive::Type prim_type )
{
	Pool *poolp = FindPrimPool( matep, prim_type );

	if ( poolp )
		return poolp - &_pools[0];

	return -1;
}

//==================================================================
void DispList::refreshPoolPtrs()
{
	int	offset = 0;
	for (int i=0; i < _n_pools; ++i)
	{
		_pools[i]._pool_prims_offset = offset;
		offset += _pools[i]._pool_n_verts;
	}
}

//==================================================================
void DispList::AllocIndices( int n_prim_indices )	throw(...)
{
	if ERR_NULL( PSYS_REALLOC2( (void **)&_prim_indices_datap, n_prim_indices*sizeof(u_short) ) )
		throw "AllocIndices failed to alloc";

	_max_prim_indices = n_prim_indices;
}

//==================================================================
int DispList::rebuild_add_vertices_triangles( const Triangle *trigs_list_frontp, u_int n_trigs,
												bool has_tex,
												bool has_col,
												bool has_nor,
												VMERGE_t *vmergep )
{
	VtxBundle	idxs;
	int	old_n_idxs;

	old_n_idxs = vmergep->idxs.size();

	// create a pool of common indices
	for (int vi=0; vi < n_trigs; ++vi, ++trigs_list_frontp)
	{
		idxs.Reset();
		
		// add 3 indices-groups for every triangle
		for (int i=0; i < 3; ++i)
		{
							idxs._pa0 = trigs_list_frontp->pos[i];
			if ( has_tex )	idxs._pa1 = trigs_list_frontp->tex[i];
			if ( has_col )	idxs._pa2 = trigs_list_frontp->col[i];
			if ( has_nor )	idxs._pa3 = trigs_list_frontp->nor[i];

			VMERGE_add_vert( vmergep, &idxs );
		}
	}

	return vmergep->idxs.size() - old_n_idxs;
}

//==================================================================
// skinning unsupported for the time being
/*
	u_char	*bnl_destp = NULL;
	float	*wei_destp = NULL;

		if ( xshapep->bonelistp && xshapep->weightlistp )
		{
			m5d_mesh_verts_maxweights_set( mgrp, xshapep->max_weights );
			bnl_destp = m5d_mesh_verts_bnlist_get( mgrp, start_idx );
			wei_destp = m5d_mesh_verts_weights_get( mgrp, start_idx );
		}
		for (i=0; i < vmergep->idxs.size(); ++i)
		{
			sidxp = &vmergep->idxs[i*VMERGE_MAXINDICES];
			if ( bnl_destp && wei_destp )
			{
			int		*sbnlp = xshapep->bonelistp   + sidxp[0] * xshapep->max_weights;
			float	*sweip = xshapep->weightlistp + sidxp[0] * xshapep->max_weights;

				for (j=0; j < xshapep->max_weights; ++j)
				{
					bnl_destp[ i * xshapep->max_weights + j ] = sbnlp[ j ];
					wei_destp[ i * xshapep->max_weights + j ] = sweip[ j ];
				}
			}
		}
*/

//==================================================================
void DispList::rebuildCreateVerts(
						u_char				*src_datap[4],
						const u_int			src_format[4],
						VMERGE_t			*vmergep )
{
	// easy to calculate
	int	src_size[VType::N];
	// vertex element size = (sizeof(type) * count (same for src and dest))
	for (int i=0; i < VType::N; ++i)
		src_size[i] = DListVertPool::CalcVertSize( i, src_format[i] );

	//-- do the processing
	for (int i=0; i < _vert_def._n_verts; ++i)
	{
		// the remapped vertices permutations
		VtxBundle	&sidx = vmergep->idxs[ i ];

		// one element at the time
		for (int j=0; j < VType::N; ++j)
		{
			if NOT( _vert_def.IsActiveUsage( (VType::VType)j ) )
				continue;

			int idx = sidx[j];

			PASSERT( idx >= 0 );

			_vert_def.CopyVert( i, (VType::VType)j,
								src_datap[j] + idx * src_size[j],
								src_format[j] );
		}
	}
}

//==================================================================
void DispList::rebuildVertsForVtxlist(
						u_char				*src_datap[4],
						const u_int			src_format[4],
						const Triangle	*trigs_list_frontp,
						int					n_trigs )
{
	// easy to calculate
	int	src_size[VType::N];
	// vertex element size = (sizeof(type) * count (same for src and dest))
	for (int i=0; i < VType::N; ++i)
		src_size[i] = DListVertPool::CalcVertSize( i, src_format[i] );

	//-- do the processing
	for (int i=0; i < n_trigs; ++i)
	{
		// one element at the time
		for (int j=0; j < VType::N; ++j)
		{
			if ( !_vert_def.IsActiveUsage( (VType::VType)j ) || !src_datap[j] )
				continue;

			for (int k=0; k < 3; ++k)
			{
				_vert_def.CopyVert( i * 3 + k, (VType::VType)j,
									src_datap[j] + trigs_list_frontp[i].idx[j][k] * src_size[j],
									src_format[j] );
			}
		}
	}
}

//==================================================================
void DispList::setupPoolsFromTriangles( const Triangle *trigs_list_frontp, int n_trigs ) throw(...)
{
	const Triangle *trip = trigs_list_frontp;
	for (int i=0; i < n_trigs; ++i, ++trip)
	{
		Pool	*poolp;

		if NOT( trip->materialp )
		{
			throw "No Material !!!";
		}

		if NOT( poolp = FindPrimPool( trip->materialp, Primitive::TYPE_TRIANGLELIST ) )
			if ERR_NULL( poolp = addPrimPoolRaw( trip->materialp, Primitive::TYPE_TRIANGLELIST, 0 ) )
			{
				throw "Out of pools !";
			}

		poolp->_pool_n_verts += 3;
	}

	refreshPoolPtrs();
}

//==================================================================
void DispList::rebuildCreateTrigs( const Triangle *trigs_list_frontp, int n_trigs, VMERGE_t *vmergep ) throw(...)
{
	AllocIndices( n_trigs * 3 );

	setupPoolsFromTriangles( trigs_list_frontp, n_trigs );

	// the used prims value matches the max indices allocated
	_max_used_verts = GetMaxVerts();

	//--- now proceed to add the triangles into the pools
	int	cur_tri_idx[ MAX_PRIMS_POOLS ] = { 0 };	// fill to 0

	const Triangle *trip = trigs_list_frontp;
	for (int i=0; i < n_trigs; ++i, ++trip)
	{
		if NOT( trip->materialp )
		{
			throw "No Material !!!";
		}

		int	pool_idx = FindPrimPoolIdx( trip->materialp, Primitive::TYPE_TRIANGLELIST );
		if ( pool_idx < 0 )
		{
			throw "Impossibility finding a pool !";
		}

		// create the displist triangle from the source triangle
		u_short	*dest_trip = GetFirstIndexPtr() + GetPrimPool( pool_idx )->GetOffset() + cur_tri_idx[ pool_idx ]++ * 3;

		VtxBundle	idxs;
		idxs.Reset();

		for (int j=0; j < 3; ++j)
		{
			// VMERGE will find the matching unique vertex
			if ( _vert_def.IsActiveUsage( VType::POS ) )	idxs[0] = trip->pos[ j ];
			if ( _vert_def.IsActiveUsage( VType::TEX ) )	idxs[1] = trip->tex[ j ];
			if ( _vert_def.IsActiveUsage( VType::COL ) )	idxs[2] = trip->col[ j ];
			if ( _vert_def.IsActiveUsage( VType::NOR ) )	idxs[3] = trip->nor[ j ];

			int unique_vertex_idx = VMERGE_find_vert_sub( vmergep, 0, vmergep->idxs.size(), idxs );

			// impossible !!!
			if ERR_FALSE( !(unique_vertex_idx & VMERGE_NOMATCH_FLAG) )
			{
				throw "Impossibility creating triangles !";
			}

			dest_trip[j] = unique_vertex_idx;
		}
	}
}

//==================================================================
void DispList::freeDList()
{
	_vert_def.FreeData();

	SAFE_FREE( _prim_indices_datap );
	_max_prim_indices = 0;

	_n_pools = 0;
}

//==================================================================
void	DispList::RebuildTriIDDList( Triangles &trigs, u_int start_id,
									 const Vector3	*src_posp ) throw(...)
{
	int n_trigs = trigs._list.size();

	// free-up whatever has been allocated before
	freeDList();

	try {
		AllocIndices( n_trigs * 3 );
		AddPrimPool( NULL, Primitive::TYPE_TRIANGLELIST, n_trigs * 3 );
		ReallocPackVerts( PackVert::TYPE_POSCOL, n_trigs * 3 );
	} catch (...) {
		throw;
	}

	const Triangle *trigs_list_frontp = &trigs._list.front();

	PackVert::PosCol	*vertsp = (PackVert::PosCol *)BeginVerts();
	u_short				*des_trigp = GetFirstIndexPtr() + GetPrimPool(0)->GetOffset();

	u_int	trivert_idx = 0;
	u_int	cur_id = start_id;

	const Triangle *trigs_list_frontp_end = trigs_list_frontp + n_trigs;
	for (; trigs_list_frontp != trigs_list_frontp_end; ++trigs_list_frontp, trivert_idx += 3)
	{
		// do tri indices
		des_trigp[0] = trivert_idx + 0;
		des_trigp[1] = trivert_idx + 1;
		des_trigp[2] = trivert_idx + 2;
		des_trigp += 3;

		// do verts
		vertsp[0].pos = src_posp[ trigs_list_frontp->pos[0] ];
		vertsp[1].pos = src_posp[ trigs_list_frontp->pos[1] ];
		vertsp[2].pos = src_posp[ trigs_list_frontp->pos[2] ];

		vertsp[0].col.Set( cur_id );
		vertsp[1].col.Set( cur_id );
		vertsp[2].col.Set( cur_id );

		vertsp += 3;
		++cur_id;
	}

	EndVerts();
}

//==================================================================
void	DispList::RebuildOnOffDList( Triangles &trigs,
									 const u_char *tri_onoffmap, int idx_base,
									 u_int col_on, u_int col_off,
									 const Vector3	*src_posp,
									 const Vector3	*src_norp ) throw(...)
{
	int n_trigs = trigs._list.size();

	// free-up whatever has been allocated before
	freeDList();

	try {
		AllocIndices( n_trigs * 3 );
		AddPrimPool( NULL, Primitive::TYPE_TRIANGLELIST, n_trigs * 3 );
		ReallocPackVerts( PackVert::TYPE_POSCOL, n_trigs * 3 );
	} catch (...) {
		throw;
	}

	const Triangle *trigs_list_frontp = &trigs._list.front();

	// Begin building verts
	PackVert::PosCol	*vertsp = (PackVert::PosCol *)BeginVerts();

	u_short			*des_trigp = GetFirstIndexPtr() + GetPrimPool(0)->GetOffset();

	u_int	trivert_idx = 0;
	u_int	idx = idx_base;

	const Triangle *trigs_list_frontp_end = trigs_list_frontp + n_trigs;
	for (; trigs_list_frontp != trigs_list_frontp_end; ++trigs_list_frontp, trivert_idx += 3, ++idx)
	{
		// do tri indices
		des_trigp[0] = trivert_idx + 0;
		des_trigp[1] = trivert_idx + 1;
		des_trigp[2] = trivert_idx + 2;
		des_trigp += 3;

		// do verts
		vertsp[0].pos = src_posp[ trigs_list_frontp->pos[0] ];
		vertsp[1].pos = src_posp[ trigs_list_frontp->pos[1] ];
		vertsp[2].pos = src_posp[ trigs_list_frontp->pos[2] ];

		u_int	col = ( tri_onoffmap[idx/8] & (1<<(idx&7)) ) ? col_on : col_off;
		// same color for all 3 verts
		vertsp[0].col.Set( col );
		vertsp[1].col.Set( col );
		vertsp[2].col.Set( col );

		vertsp += 3;
	}

	// end building verts
	EndVerts();
}

//==================================================================
void	DispList::UpdateOnOffDList( const u_char *tri_onoffmap, int idx_base,
									u_int col_on, u_int col_off )
{
	LockW( __FILE__, __LINE__ );

	PackVert::PosCol	*vertsp = (PackVert::PosCol *)BeginVerts();

	int		n_trigs = GetPrimPool(0)->GetVertsN() / 3;	// $$$ ehh we /3 recently, but not tested

	PASSERT( GetPrimPool(0)->GetVertsN() == GetPrimPool(0)->GetPrimsN()*3 );

	for (int i=0; i < n_trigs; ++i)
	{
		int		idx = i + idx_base;

		u_int	col = ( tri_onoffmap[idx/8] & (1<<(idx&7)) ) ? col_on : col_off;

		// same color for all 3 verts
		vertsp[0].col.Set( col );
		vertsp[1].col.Set( col );
		vertsp[2].col.Set( col );
		vertsp += 3;
	}

	EndVerts();

	Unlock();
}

//==================================================================
void DispList::RebuildPosOnlyDList(	Triangles &trigs, const Vector3 *src_posp, int n_verts )
{
	int n_trigs = trigs._list.size();

	// free-up whatever has been allocated before
	freeDList();

	LockW( __FILE__, __LINE__ );

	try {
		AllocIndices( n_trigs * 3 );
		AddPrimPool( NULL, Primitive::TYPE_TRIANGLELIST, n_trigs * 3 );
		ReallocPackVerts( PackVert::TYPE_POS, n_trigs * 3 );
	} catch (...) {

		Unlock();
		throw;
	}

	u_short		*des_trigp = GetFirstIndexPtr() + GetPrimPool(0)->GetOffset();

	const Triangle *trigs_list_frontp = &trigs._list.front();

	// copy triangles
	const Triangle *trigs_list_frontp_end = trigs_list_frontp + n_trigs;
	for (; trigs_list_frontp != trigs_list_frontp_end; ++trigs_list_frontp)
	{
		// do tri indices
		des_trigp[0] = trigs_list_frontp->pos[0];
		des_trigp[1] = trigs_list_frontp->pos[1];
		des_trigp[2] = trigs_list_frontp->pos[2];
		des_trigp += 3;
	}

	// copy verts
	PackVert::Pos	*vertsp = (PackVert::Pos *)BeginVerts();
	PackVert::Pos	*vertsp_end = vertsp + n_verts;
	for (; vertsp < vertsp_end; ++vertsp)
	{
		vertsp->pos = src_posp[0];
		++src_posp;
	}
	EndVerts();

	Unlock();
}

//==================================================================
void DispList::RebuildIdxDList(	Triangles &trigs, int n_trigs,
							   const Vector3	*src_posp, u_int pos_out_vf,
							   const Vector2	*src_texp, u_int tex_out_vf,
							   const Color4b	*src_colp, u_int col_out_vf,
							   const Vector3	*src_norp, u_int nor_out_vf )
{
	VMERGE_t	vmerge;

	// free up whatever has been allocated before
	freeDList();

	LockW( __FILE__, __LINE__ );

//	const Triangle *trigs_list_frontp = &trigs._list.front();
	const Triangle *trigs_list_frontp = &trigs._list[0];

	try {
		// we need position vertices at the very least !!!
		if ERR_NULL( src_posp != NULL )
			throw "src_posp is NULL !";

		u_char	*src_datap[4];
		src_datap[0] = (u_char *)src_posp;
		src_datap[1] = (u_char *)src_texp;
		src_datap[2] = (u_char *)src_colp;
		src_datap[3] = (u_char *)src_norp;

		u_int	src_format[4];
		src_format[0] = DListVertPool::FORMAT_FLOAT;
		src_format[1] = DListVertPool::FORMAT_FLOAT;
		src_format[2] = DListVertPool::FORMAT_BYTE;
		src_format[3] = DListVertPool::FORMAT_FLOAT;

		//-------------
		int n_added = rebuild_add_vertices_triangles( trigs_list_frontp, n_trigs,
													(src_datap[1] != NULL),
													(src_datap[2] != NULL),
													(src_datap[3] != NULL),
													&vmerge );
		if ERR_FALSE( n_added > 0 )
			throw;

		//------ proceed to add vertices
		if ERR_FALSE( vmerge.idxs.size() > 0 )
			throw;

		// dest format
		_vert_def.Reset();
		if ( src_datap[0] )		_vert_def.AddItem( pos_out_vf, VType::POS );
		if ( src_datap[1] )		_vert_def.AddItem( tex_out_vf, VType::TEX );
		if ( src_datap[2] )		_vert_def.AddItem( col_out_vf, VType::COL );
		if ( src_datap[3] )		_vert_def.AddItem( nor_out_vf, VType::NOR );
		_vert_def.FreeData();
		_vert_def.AllocateData( vmerge.idxs.size() );

		// create new vertex data
		rebuildCreateVerts( src_datap, src_format, &vmerge );
		rebuildCreateTrigs( trigs_list_frontp, n_trigs, &vmerge );

		Unlock();

	}catch (...) {
		Unlock();

		freeDList();
		throw "ouuuu !";
	}
}

//==================================================================
void DispList::RebuildVtxDList(	Triangles &trigs, int n_trigs,
							   const Vector3	*src_posp, u_int pos_out_vf,
							   const Vector2	*src_texp, u_int tex_out_vf,
							   const Color4b	*src_colp, u_int col_out_vf,
							   const Vector3	*src_norp, u_int nor_out_vf,
							   const bool *foced_vtypesp )
{
	// free up whatever has been allocated before
	freeDList();

	LockW( __FILE__, __LINE__ );

//	const Triangle *trigs_list_frontp = &trigs._list.front();
	const Triangle *trigs_list_frontp = &trigs._list[0];

	try {
		// we need position vertices at the very least !!!
		if ERR_NULL( src_posp != NULL )
			throw "src_posp is NULL !";

		u_char	*src_datap[4];
		src_datap[0] = (u_char *)src_posp;
		src_datap[1] = (u_char *)src_texp;
		src_datap[2] = (u_char *)src_colp;
		src_datap[3] = (u_char *)src_norp;

		u_int	src_format[4];
		src_format[0] = DListVertPool::FORMAT_FLOAT;
		src_format[1] = DListVertPool::FORMAT_FLOAT;
		src_format[2] = DListVertPool::FORMAT_BYTE;
		src_format[3] = DListVertPool::FORMAT_FLOAT;

		// dest format
		_vert_def.FreeData();
		_vert_def.Reset();
		if ( src_datap[0] || (foced_vtypesp && foced_vtypesp[0]) )	_vert_def.AddItem( pos_out_vf, VType::POS );
		if ( src_datap[1] || (foced_vtypesp && foced_vtypesp[1]) )	_vert_def.AddItem( tex_out_vf, VType::TEX );
		if ( src_datap[2] || (foced_vtypesp && foced_vtypesp[2]) )	_vert_def.AddItem( col_out_vf, VType::COL );
		if ( src_datap[3] || (foced_vtypesp && foced_vtypesp[3]) )	_vert_def.AddItem( nor_out_vf, VType::NOR );
		_vert_def.AllocateData( n_trigs * 3 );
		_max_used_verts = _vert_def._n_verts;

		setupPoolsFromTriangles( trigs_list_frontp, n_trigs );
		rebuildVertsForVtxlist( src_datap, src_format, trigs_list_frontp, n_trigs );

		Unlock();

	}catch (...) {
		Unlock();

		freeDList();
		throw "ouuuu !";
	}
}

//==================================================================
u_short	*M5D::DispList::BeginAddIndexPrims( Material *matep, Primitive::Type prim_type, int n_prim_verts/*=0 */ )
{
	AssertLockW();
#ifdef PARANOIA_CHECKS
	PASSERT( GetPrimPoolsN() < MAX_PRIMS_POOLS );
	PASSERT( GetFirstIndexPtr() != NULL );
#endif
	Pool *poolp = AddPrimPool( matep, prim_type, n_prim_verts > 0 ? n_prim_verts : GetMaxIndices() );
	return poolp ? GetFirstIndexPtr() + poolp->GetOffset() : NULL;
}

//==================================================================
void M5D::DispList::EndAddIndexPrims()
{
	AssertLockW();
}

//==================================================================
void *M5D::DispList::BeginAddVertexPrims( Material *matep, Primitive::Type prim_type, int n_prim_verts/*=0 */ )
{
	AssertLockW();
	PASSERT( GetPrimPoolsN() < MAX_PRIMS_POOLS );

	Pool *poolp = AddPrimPool( matep, prim_type, n_prim_verts > 0 ? n_prim_verts : GetMaxVerts() );

	return BeginVerts( poolp ? poolp->GetOffset() : 0 );
}

//==================================================================
void M5D::DispList::EndAddVertexPrims( int updated_n_prim_verts/*=-1 */ )
{
	if ( updated_n_prim_verts >= 0 )
	{
		Pool *cur_poolp = &_pools[ _n_pools - 1 ];
		if ERR_FALSE( updated_n_prim_verts <= cur_poolp->_pool_n_verts )
		{
			AssertLockW();
			EndVerts();
			return;
		}
		// update current pool
		cur_poolp->_pool_n_verts = updated_n_prim_verts;

		// update total vertex count
		_max_used_verts = cur_poolp->_pool_n_verts;
		if ( _n_pools >= 2 )
		{
			Pool *prev_poolp = &_pools[ _n_pools - 2 ];
			_max_used_verts += prev_poolp->_pool_prims_offset + prev_poolp->_pool_n_verts;
		}
	}
	AssertLockW();
	EndVerts();
}
